package edu.washington.cs.workflow.recommender;

import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import edu.washington.cs.workflow.analysis.guistructure.UIActionEntity;
import edu.washington.cs.workflow.util.Utils;

public class MethodSummarizer {

	public final String oldTraceFile;
	public final List<String> newTraceFiles;
	
	public final EventTraceProcessor oldProcessor;
	public final EventTraceProcessor[] newProcessors;
	
	public MethodSummarizer(String oldTraceFile, String... newTraceFiles) {
		this(oldTraceFile, Arrays.asList(newTraceFiles));
	}
	
	public MethodSummarizer(String oldTraceFile, List<String> newTraceFiles) {
		Utils.checkNotNull(oldTraceFile);
		Utils.checkNotNull(newTraceFiles);
		Utils.checkTrue(!newTraceFiles.isEmpty());
		this.oldTraceFile = oldTraceFile;
		this.newTraceFiles = newTraceFiles;
		//initialize the processors
		//initialize the processors
		this.oldProcessor = new EventTraceProcessor(this.oldTraceFile);
		this.newProcessors = new EventTraceProcessor[this.newTraceFiles.size()];
		for(int i = 0; i < newProcessors.length; i++) {
			newProcessors[i] = new EventTraceProcessor(this.newTraceFiles.get(i));
		}
	}
	
	public List<String> getAllNewActionCandidates() {
		List<String> actionList = new LinkedList<String>();
		for(EventTraceProcessor processor : this.newProcessors) {
			Set<String> actionSet = processor.getUniqueActionDesc();
			actionList.addAll(actionSet);
		}
		return actionList;
	}
	
	public List<String> getInvokedMethodsByNewAction(String actionDesc) {
		List<String> methods = new LinkedList<String>();
		
		for(EventTraceProcessor processor : this.newProcessors) {
			List<String> ms = processor.getInvokedMethodsByAction(actionDesc);
			methods.addAll(ms);
		}
		
		return methods;
	}
	
	public List<MethodStatistic> getRankedMethodStatistics() {
		List<MethodStatistic> stats = this.summarizeMethodStatisticsInReverseHandlerOrder();
		Collections.<MethodStatistic>sort(stats);
		return stats;
	}
	
	public List<MethodStatistic> summarizeMethodStatisticsInReverseHandlerOrder() {
		List<String> uniqueHandlers = oldProcessor.getAllUniqueActionHandlerDescs();
		Collections.reverse(uniqueHandlers); //reverse it
		
		List<String> allMethods = new LinkedList<String>();
		for(String handlerDesc : uniqueHandlers) {
			allMethods.add(handlerDesc);
			//its invoked method
			List<String> invokedMethods = oldProcessor.getInvokedMethodsByHandler_fast(handlerDesc);
			allMethods.addAll(invokedMethods);
		}
		
		//see the method invocation order
		List<MethodStatistic> stats = this.summarizeMethodStatistics(allMethods);
		
		return stats;
	}
	
	public List<MethodStatistic> summarizeMethodStatisticsInReverse() {
		List<String> allMethods = oldProcessor.getAllUniqueMethods();
		Collections.reverse(allMethods);
		return summarizeMethodStatistics(allMethods);
	}
	
	public List<MethodStatistic> summarizeMethodStatistics() {
		List<String> allMethods = oldProcessor.getAllUniqueMethods();
		return summarizeMethodStatistics(allMethods);
	}
	
	public List<MethodStatistic> summarizeMethodStatistics(List<String> allMethods) {
		List<MethodStatistic> meList = new LinkedList<MethodStatistic>();
		for(String methodDesc : allMethods) {
			//for each method, it creates a MethodStatistic object
			MethodStatistic stats = this.createMethodStatistic(methodDesc);
			Utils.checkNotNull(stats);
			meList.add(stats);
		}
		
		return meList;
	}
	
	public MethodStatistic createMethodStatistic(String methodDesc) {
		
		Set<String> handlerSet = new LinkedHashSet<String>();
		for(EventTraceProcessor p : this.newProcessors) {
			Set<String> hS = p.getIncludingHandlers(methodDesc);
			if(hS == null) {
				continue;
			}
			handlerSet.addAll(hS);
		}
		
		Set<String> actionSet = new LinkedHashSet<String>();
		for(EventTraceProcessor p : this.newProcessors) {
			Set<String> aS = p.getTriggeringActions_fast(methodDesc);
			if(aS == null) {
				continue;
			}
			actionSet.addAll(aS);
		}
		
		return new MethodStatistic(methodDesc, handlerSet, actionSet);
	}
	
	//for each method, summarize the matched event handler num, the matched action num,
	class MethodStatistic implements Comparable {
		public final String methodDesc;
		public final int numOfHandlers;
		public final int numOfActions;
		public final Collection<String> handlers;
		public final Collection<String> actions;
		public MethodStatistic(String methodDesc, Collection<String> hs, Collection<String> as) {
			Utils.checkNotNull(methodDesc);
			Utils.checkNotNull(hs);
			Utils.checkNotNull(as);
			this.methodDesc = methodDesc;
			this.handlers = hs;
			this.actions = as;
			this.numOfHandlers = hs.size();
			this.numOfActions = as.size();
		}
		
		@Override
		public String toString() {
			return methodDesc  + ", num of handlers: " + this.numOfHandlers
			    + ", num of actions: " + this.numOfActions + "\n     handlers:" + this.handlers
			    + "\n    actions: " + this.actions;
		}

		@Override
		public int compareTo(Object o) {
			if(!(o instanceof MethodStatistic)) {
				throw new Error("Cannot compare with a different type.");
			}
			MethodStatistic another = (MethodStatistic)o;
			
			if(this.numOfActions < another.numOfActions) {
				return -1;
			} else if (this.numOfActions == another.numOfActions) {
				return this.numOfHandlers - another.numOfHandlers;
			} else {
				return 1;
			}
		}
	}
}
